//
// core - server core source code.
//
// $Rev$
// $Author$
// $Date$
//
// Define class TCPClient.
//

#include "core/tcp_client.h"

#include <boost/bind.hpp>

#include "core/configure.h"
#include "core/connector.h"
#include "core/tcp_connection.h"
#include "core/base/auto_mutex.h"
#include "core/base/logging.h"

namespace core {

TCPClient::TCPClient(const InetAddress &server_address,
       const std::string &name)
       : connected_(false), retry_(true), name_(name),
         loop_(NULL), connector_(NULL), connection_(NULL),
         server_address_(server_address) {}

TCPClient::~TCPClient() {
  {
    AutoMutex auto_mutex(&this->mutex_);
    if(this->connection_) {
      delete this->connection_;
      this->connection_ = NULL;
    }
  }
  if(this->connector_) {
    delete this->connector_;
    this->connector_ = NULL;
  }
}

bool TCPClient::Initialize(EventLoop *loop) {
  assert(loop);
  this->loop_ = loop;
  this->connector_ = new Connector(this->loop_,
         this->server_address_, Configure::GetConnector()->retry_msec_);
  assert(this->connector_);
  this->connector_->SetNewConnectionCallback(
         boost::bind(&TCPClient::NewTCPConnection, this, _1, _2));
  return true;
}

void TCPClient::Start() {
  if(this->connected_ == false) {
    this->connector_->Start();
  }
}

void TCPClient::Stop() {
  this->retry_ = false;
  if(this->connected_ == false) {
    this->connector_->Stop();
  } else {
    AutoMutex auto_mutex(&this->mutex_);
    if(this->connection_)
      this->connection_->Shutdown();
  }
}

void TCPClient::NewTCPConnection(int sockfd, const InetAddress &local) {
  char buffer[16];
  snprintf(buffer, sizeof(buffer), ":%d", sockfd);
  std::string connection_name = this->name_ + buffer;
  TCPConnection *connection = new TCPConnection(sockfd, this->loop_,
         connection_name, local, this->server_address_);
  if(connection == NULL) {
    CoreLog(ERROR, "%s:%d (%s) Client[%s] failed to allocate tcp connection.",
         __FILE__, __LINE__, __FUNCTION__, this->name_.c_str());
    return ;
  }
  if(connection->Initialize() == false) {
    CoreLog(ERROR, "%s:%d (%s) Client[%s] failed to initialize tcp connection.",
         __FILE__, __LINE__, __FUNCTION__, this->name_.c_str());
    delete connection;
    return ;
  }
  connection->SetConnectionCallback(this->connection_callback_);
  connection->SetMessageCallback(this->message_callback_);
  connection->SetWriteCompleteCallback(this->write_complete_callback_);
  connection->SetCloseCallback(boost::bind(&TCPClient::RemoveTCPConnection, this, _1));
  connection->ConnectionEstablished();
  this->connected_ = true;
}

void TCPClient::RemoveTCPConnection(const TCPConnection *connection) {
  this->connected_ = false;
  assert(connection == this->connection_);
  {
    AutoMutex auto_mutex(&this->mutex_);
    this->connection_->ConnectionDestoryed();
    delete this->connection_;
    this->connection_ = NULL;
  }
  if(this->retry_) {
    this->connector_->Restart();
  }
}

}  // namespace core

